home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Disc to the Future 2
/
Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin
/
MAC
/
THINKC
/
4_0
/
VIVIDUS
/
QD3D.SIT
/
qd3d
/
gouraud.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-08-17
|
13KB
|
497 lines
#include <math.h>
#include "gouraud.h"
#include <Cqd3dPort.h>
int next (int n, FixedVector v[], int s);
int prev (int n, FixedVector v[], int s);
/* ======================================================================
Gouraud and line color blending primitives.
This is part of the qd3d Vividus Source Code Library. See the
extern qd3d.doc documentation file for usage. See individual
routines for routine documentation.
Copyright 1991 by Vividus Consulting.
This is not public domain source code. You may not copy and
paste from this source code. Read your Vividus Licensing
agreement for details and other restrictions.
====================================================================== */
/* ======================================================================
WARNING to qd3d users:
Everything in this file is private to the qd3d library and highly
subject to change.
====================================================================== */
/* ======================================================================
Explanatory notes:
The qd3d routines in this "gouraud.c" source file discretize
the "vector" points specified by the external client routines
to FixedVector points before screen discretization to "int's."
Internal calculations are calculated using the FixedVector
representation. The FixedVector representation is used to:
ensure no gaps or overlaps between adjacent polygons whose edges
possess identical endpoints; aid in prevention of gaps and overlaps
between adjacent polygons whose edges don't possess identical
endpoints -- this has not fully been evaluated yet; and ensure
that the last location specified by a dda ends up at the correct
location -- it increases the accuracy of the dda. Since
MC68K processors are internally 32 bits, this doesn't
drastically decrease performance. Details of the application
of this concept are considered proprietary and will not be
discussed further.
The qd3d routines in this "gouraud.c" source file also use a
FixedVector discretization of the client indicated color vector.
This FixedVector representation is the "vector" times 2^15.
Two^15 is used instead of 2^16 to avoid sign problems.
====================================================================== */
extern unsigned int *theZBuff;
extern unsigned int *theZBuffLast;
extern unsigned int theZBuffWidth;
#define abs(a) ((a) > 0 ? (a) : -(a))
/*
Some optimization timings:
Note: As presently implemented (<910618), the bottleneck for the Gouraud shader
is SetCPixel. Some timings:
25.67 sec. When used with SetCPixel.
9.0x sec. Doing everything except calling SetCPixel.
4.5 sec. Poly's drawn with the xdraw debug code.
<2 sec. Poly's drawn with "PaintPoly."
Plot3dPoint is an ideal change point.C
*/
/* ------------------------------------------------------------ */
/* Routines: */
void
PlotC3dPoint(FixedVector *x, FixedVector *c)
/*
Plot the 3d point at x with the color identified by c.
Warning: This is a private function to the qd3d library and is
highly subject to change or removal.
*/
{
register unsigned int *q;
RGBColor rgb;
IntVector pp;
fv2iv(x, &pp);
cv2rgb(c, &rgb);
/* plot 3d point */
if (theZBuff) {
q = theZBuff;
q += (unsigned long)pp.y * theZBuffWidth;
q += (unsigned long)pp.x;
if ( (q < theZBuffLast) && (q >= theZBuff) ) {
if (pp.z <= *q) {
*q = pp.z;
SetCPixel(pp.x, pp.y, &rgb);
}
}
} else {
SetCPixel(pp.x, pp.y, &rgb);
}
}
void
C3dLine(FixedVector *p1, FixedVector *p2, FixedVector *c1, FixedVector *c2)
/*
This routine draws a color blended line segment from p1 to p2 where
p1 has color c1 and c2 has color c2.
Warning: This is a private function to the qd3d library and is
highly subject to change or removal.
*/
{
FixedVector c, dc;
FixedVector x, dx;
Boolean xmajor; /* xmajor indicates the slope of the line is < 1. */
/* this indicate the line is "xmajor." Otherwise, */
/* it is "ymajor." */
int n;
Fixed rw;
int i;
FixedVector fv;
Fixed alpha;
Boolean agt0;
static Boolean Debug = false;
if (Debug) { /* Speeds up overlap debugging */
ForeColor(blackColor);
PenMode(patXor);
MoveTo(HiWord(p1->x), HiWord(p1->y));
LineTo(HiWord(p2->x), HiWord(p2->y));
return;
}
fvsub(p2, p1, &dx);
fvsub(c2, c1, &dc);
xmajor = abs(dx.x) >= abs(dx.y);
if (xmajor) {
rw = FixDiv(0x10000L, abs(dx.x));
n = abs(HiWord(p2->x) - HiWord(p1->x));
} else {
rw = FixDiv(0x10000L, abs(dx.y));
n = abs(HiWord(p2->y) - HiWord(p1->y));
}
fvscale(rw, &dx, &dx);
fvscale(rw, &dc, &dc);
/* dx.y or dx.x should == 1 or there will be
cumulative roundoff errors.
*/
/* Adjust so that increments will hit on xmajor half pixels. */
/* This is necessary for this routine to be compatable with the */
/* Polygon shader. */
if (xmajor) {
alpha = (p1->x & 0xffff0000) | 0x00008000L;
alpha -= p1->x;
if (dx.x > 0) { /* force |dx.x| = 1 */
dx.x = 0x10000;
} else {
dx.x = 0xffff0000;
alpha = -alpha;
}
} else {
alpha = (p1->y & 0xffff0000) | 0x00008000L;
alpha -= p1->y;
if (dx.y > 0) { /* force |dx.y| = 1 */
dx.y = 0x10000;
} else {
dx.y = 0xffff0000;
alpha = -alpha;
}
dx.y = (dx.y > 0) ? 0x10000 : 0xffff0000;
}
fvscale(alpha, &dx, &fv);
fvadd(&fv, p1, &x); /* x.x or x.y should end up xxx.5 */
fvscale(alpha, &dc, &fv);
fvadd(&fv, c1, &c);
/* If necessary, draw the first point. */
if ( (HiWord(p1->x) != HiWord(x.x)) || (HiWord(p1->y) != HiWord(x.y)) )
PlotC3dPoint(p1, c1);
/* Draw the line */
for (i = 0; i < n; i++) {
PlotC3dPoint(&x, &c);
fvadd(&c, &dc, &c);
fvadd(&x, &dx, &x);
}
if ( (HiWord(x.y) != HiWord(p2->y)) && !xmajor)
DebugStr("\pC3dline didn't end on correct point!");
if ( xmajor && (HiWord(x.x) != HiWord(p2->x)) )
DebugStr("\pC3dline didn't end on correct point!");
#if 0
if ( (HiWord(x.y) != HiWord(p2->y)) || (HiWord(x.x) != HiWord(p2->x)) )
DebugStr("\pC3dline cum roundoff error? didn't end on correct point!");
#endif
}
/* ------------------------------------------------------------ */
/* Routines for the polygon shader. */
void
ScanC3dLine(FixedVector *l, FixedVector *r, FixedVector *cl, FixedVector *cr)
/*
Draw a color blended scan line from the left l to the right r. L has a
color of cl and r has a color cr.
Note: l and r must have the same y value.
Warning: This is a private function to the qd3d library and is
highly subject to change or removal.
*/
{
FixedVector c, dc;
FixedVector x, dx;
int n = HiWord(r->x) - HiWord(l->x);
Fixed rw = Long2Fix((long)n);
int i;
static Boolean Debug = false;
if (n <= 0)
return;
if (Debug) { /* Speeds up overlap debugging */
ForeColor(blackColor);
PenMode(patXor);
MoveTo(HiWord(l->x), HiWord(l->y));
LineTo(HiWord(r->x), HiWord(r->y));
LineTo(HiWord(r->x), HiWord(r->y));
return;
}
fvcopy(cl, &c);
fvcopy(l, &x);
fvsub(r, l, &dx);
rw = FixDiv(0x10000L, rw);
fvscale(rw, &dx, &dx);
dx.x = 0x10000; /* This isn't exactly correct but it prevents cumulative
roundoff error. Ie. holes in the picture! */
dx.y = 0x00000000;
fvsub(cr, cl, &dc);
fvscale(rw, &dc, &dc);
for (i = 0; i < n; i++) {
fvadd(&x, &dx, &x);
if (i != n -1)
fvadd(&c, &dc, &c);
PlotC3dPoint(&x, &c);
}
if ( (HiWord(x.y) != HiWord(r->y)) || (HiWord(x.x) != HiWord(r->x)) )
DebugStr("\pScan line didn't end on correct point!");
}
#define succ(a) ((a + 1) % n)
#define pred(a) ( (a == 0) ? n - 1 : (a-1) )
int
next (int n, FixedVector v[], int s)
/*
Returns the index into v of the next vertice in the polygon given
that the present vertice is s. N is the number of vertices.
If the value returned is < 0, the vertice tranversal has finished.
Note: N must be greater than 1.
Warning: This is a private function to the qd3d library and is
highly subject to change or removal.
*/
{
int i = succ(s);
while ((v[i].y == v[s].y) && (v[i].x == v[s].x) && (v[i].z == v[s].z)) {
if (i == s)
return (-1);
i = succ(i);
}
if (v[i].y < v[s].y)
return (-1);
return(i);
}
int
prev (int n, FixedVector v[], int s)
/*
Returns the index into v of the previous vertice in the polygon given
that the present vertice is s. N is the number of vertices.
If the value returned is < 0, the vertice tranversal has finished.
Note: N must be greater than 1.
Warning: This is a private function to the qd3d library and is
highly subject to change or removal.
*/
{
int i = pred(s);
while ((v[i].y == v[s].y) && (v[i].x == v[s].x) && (v[i].z == v[s].z)) {
if (i == s)
/* Must have been a single horz line. it won't be plotted. */
return (-1);
i = pred(i);
}
if (v[i].y < v[s].y) /* if headed up, the polygon is finished. */
return (-1);
return(i);
}
void
GouraudShade(int n, FixedVector v[], FixedVector c[])
/*
Draws a Gouraud shaded (bi-linear color blend) polygon given the n
vertices v and the associated vertex colors c.
Note: This will only work on convex polygons.
Warning: This is a private function to the qd3d library and is
highly subject to change or removal.
*/
{
/*
Note: Whether the left side is really the left side is ambiguous. The
only effect this ambiguity has is the parameter order of calling
the line fill function. Leaving it ambiguous makes the code
simpler.
*/
/* Right side values. */
int ri; /* Current side target vertice index. */
int rCt; /* lines in the dda for this segment. */
FixedVector
rx, rdx, /* Position and differential. */
rc, rdc, /* Color and differential. */
rp, rpc; /* Plot position and color. */
/* Left side values. */
int li; /* Current side target vertice index. */
int lCt; /* lines in the dda for this segment. */
FixedVector
lx, ldx, /* Position and differential. */
lc, ldc, /* Color and differential. */
lp, lpc; /* Plot position and color. */
FixedVector
fv;
int ti; /* Tempory vertice index. */
Fixed tf;
Fixed alpha; /* Amount of extrapolation necessary to
get the dda's started off in the
correct place. */
int agt0; /* Alpha is > 0. */
int i;
if (n <= 2) return;
/* Find a top vertice */
ti = 0;
for (i = 0+1; i < n; i++)
if (v[i].y < v[ti].y)
ti = i;
/* Start initial ri & li */
ri = li = ti;
do {
ti = prev(n, v, ri);
if (ti < 0)
return;
if (v[ti].y == v[li].y)
ri = ti;
else
break;
} while (1);
/* Tell the dda's to start. */
rCt = lCt = 0;
/* Line by line scan conversion */
do {
if (lCt == 0) {
ti = next(n, v, li);
if (ti < 0)
break; /* It's done! */
fvsub(&v[ti], &v[li], &ldx);
fvsub(&(c[ti]), &(c[li]), &ldc);
lCt = HiWord(v[ti].y) - HiWord(v[li].y);
tf = FixDiv(Long2Fix(1L), ldx.y);
fvscale(tf, &ldc, &ldc);
fvscale(tf, &ldx, &ldx); /* ldx.y should = 1 */
ldx.y = 0x10000;
alpha = (v[li].y & 0xffff0000) | 0x00008000L; /* .5 */
alpha -= v[li].y;
agt0 = alpha > 0 ? true : false;
fvscale(alpha, &ldx, &fv);
fvadd(&fv, &v[li], &lx); /* lx.y should end up xxx.5 */
fvscale(alpha, &ldc, &fv);
fvadd(&fv, &(c[li]), &lc);
if ( !agt0 ) {
/* Use start point */
fvcopy(&(v[li]), &lp);
fvcopy(&(c[li]), &lpc);
} else {
/* Use projected point */
fvcopy(&lx, &lp);
/* fvcopy(&lc, &lpc); /* Projection may have overflowed/
underflowed color so use... */
fvcopy(&(c[li]), &lpc);
}
li = ti;
}
if (rCt == 0) {
ti = prev(n, v, ri);
if (ti < 0)
break; /* It's done! */
fvsub(&v[ti], &v[ri], &rdx);
fvsub(&(c[ti]), &(c[ri]), &rdc);
rCt = HiWord(v[ti].y) - HiWord(v[ri].y);
tf = FixDiv(Long2Fix(1L), rdx.y);
fvscale(tf, &rdc, &rdc);
fvscale(tf, &rdx, &rdx); /* rdx.y should = 1. */
rdx.y = 0x10000;
alpha = (v[ri].y &0xffff0000) | 0x00008000L; /* .5 */
alpha -= v[ri].y;
agt0 = alpha > 0 ? true : false;
fvscale(alpha, &rdx, &fv);
fvadd(&fv, &v[ri], &rx); /* rx.y should end up xxx.5 */
fvscale(alpha, &rdc, &fv);
fvadd(&fv, &(c[ri]), &rc);
if (!agt0) {
/* Use start point */
fvcopy(&(v[ri]), &rp);
fvcopy(&(c[ri]), &rpc);
} else {
/* Use projected point */
fvcopy(&rx, &rp);
/* fvcopy(&rc, &rpc); /* Projection may have overflowed/
underflowed color so use... */
fvcopy(&(c[ri]), &rpc);
}
ri = ti;
}
if ((lCt == 0) || (rCt == 0))
continue; /* This seems like a good idea. ie.
don't do edges. */
if (lp.x < rp.x)
ScanC3dLine(&lp, &rp, &lpc, &rpc);
else
ScanC3dLine(&rp, &lp, &rpc, &lpc);
/* Update the dda's */
if ((--lCt) > 0) {
fvadd(&ldx, &lx, &lx);
fvadd(&lc, &ldc, &lc);
fvcopy(&lx, &lp);
fvcopy(&lc, &lpc);
}
if ((--rCt) > 0) {
fvadd(&rdx, &rx, &rx);
fvadd(&rc, &rdc, &rc);
fvcopy(&rx, &rp);
fvcopy(&rc, &rpc);
}
} while (1);
return;
/* The following notice may not be removed under any
circumstance. See your licensing agreement. */
asm {
dc.b "gouraud Copyright 1991 Vividus Consulting"
}
}